﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Security.Permissions;
using System.Web;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Core.BOL;
using Core.DAL;
using SD = System.Drawing;

namespace Core.Utils
{
    public enum ImageMode
    {
        NotDefined,
        Home,
        GalleryThumbnail,
        GalleryPhoto,
        MyAlbumPhoto,
        MyAlbumThumbnail,
        Staff,
        StaffCrop,
        Avatar,
        Members,
        MailThumbnail
    }

    public class Imaging
    {
        #region Public Methodes
        public static Image FromByteArray(byte[] image)
        {
            using (MemoryStream ms = new MemoryStream(image, 0, image.Length))
            {
                ms.Write(image, 0, image.Length);
                return Image.FromStream(ms);
            }
        }

        public static byte[] RightRotateImage90(byte[] image)
        {
            return RotateImage90(image, EncoderValue.TransformRotate90);
        }

        public static byte[] LeftRotateImage90(byte[] image)
        {
            return RotateImage90(image, EncoderValue.TransformRotate270);
        }

        [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        public static byte[] ResizeImageToPortrait(byte[] imageData, int width, int height)
        {
            return ResizeImage(imageData, width, height, 100, true);
        }

        [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        public static byte[] ResizeImageToPortrait(byte[] imageData, int width, int height, int quality)
        {
            return ResizeImage(imageData, width, height, quality, true);
        }

        [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        public static byte[] ResizeImage(byte[] imageData, int width, int height)
        {
            return ResizeImage(imageData, width, height, 100, false);
        }

        [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        public static byte[] ResizeImage(byte[] imageData, int width, int height, int quality)
        {
            return ResizeImage(imageData, width, height, quality, false);
        }

        [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        private static byte[] ResizeImage(byte[] imageData, int width, int height, int quality, bool toPortrait)
        {
            if (imageData != null)
            {
                int originalWidth;
                int originalHeight;
                GetImageSize(imageData, out originalWidth, out originalHeight);
                if (toPortrait && (originalHeight > originalWidth))
                {
                    int maxWidth = width / 2;
                    int maxHeight = height / 2;
                    maxHeight = maxHeight + (maxHeight / 2);

                    imageData = Crop(imageData, width, height, maxWidth, maxHeight);
                }

                using (var imageStream = new MemoryStream(imageData))
                {
                    //Read the image
                    var bitmapDecoder = BitmapDecoder.Create(imageStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
                    var bitmapFrame = bitmapDecoder.Frames[0];

                    //Compute the new thumbnail size based on the image orientation
                    int thumbnailWidth, thumbnailHeight;

                    if (bitmapFrame.Width > bitmapFrame.Height)
                    {
                        thumbnailWidth = width;
                        thumbnailHeight = (int)(bitmapFrame.Height * width / bitmapFrame.Width);
                    }
                    else
                    {
                        thumbnailWidth = (int)(bitmapFrame.Width * height / bitmapFrame.Height);
                        thumbnailHeight = height;
                    }

                    //Resize the image
                    var resizedBitmapFrame = WpfFastResize(bitmapFrame, thumbnailWidth, thumbnailHeight);

                    //Convert the bitmap frame to jpg
                    byte[] targetBytes = null;

                    using (var memoryStream = new MemoryStream())
                    {
                        var targetEncoder = new JpegBitmapEncoder()
                        {
                            QualityLevel = quality
                        };

                        targetEncoder.Frames.Add(resizedBitmapFrame);
                        targetEncoder.Save(memoryStream);
                        targetBytes = memoryStream.ToArray();
                    }

                    return targetBytes;
                }
            }
            return new byte[0];
        }

        [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        public static byte[] Watermark(byte[] imageData, string watermak)
        {
            if (imageData.Length > 0)
            {
                using (var imageStream = new MemoryStream(imageData))
                {
                    //Read the image
                    var bitmapDecoder = BitmapDecoder.Create(imageStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
                    int thumbnailWidth;
                    int thumbnailHeight;
                    GetImageSize(imageData, out thumbnailWidth, out thumbnailHeight);
                    var group = new DrawingGroup();

                    RenderOptions.SetBitmapScalingMode(group, BitmapScalingMode.HighQuality);
                    var bitmapFrame = bitmapDecoder.Frames[0];
                    group.Children.Add(new ImageDrawing(bitmapFrame, new Rect(0, 0, thumbnailWidth, thumbnailHeight)));

                    //Add watermark
                    var formattedText = new FormattedText(watermak, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new System.Windows.Media.Typeface(new System.Windows.Media.FontFamily("Verdana"), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal), 11, System.Windows.Media.Brushes.Black);
                    var textBrush = new SolidColorBrush(Colors.White) { Opacity = 0.7 };

                    // Build the geometry object that represents the text.
                    var textGeometry = formattedText.BuildGeometry(new System.Windows.Point(0, 0));
                    var renderBounds = textGeometry.GetRenderBounds(new System.Windows.Media.Pen());

                    // Ration 
                    RotateTransform rotateTransform = new RotateTransform(270);
                    rotateTransform.CenterX = 57;
                    rotateTransform.CenterY = 57;
                    textGeometry.Transform = rotateTransform;
                    group.Children.Add(new GeometryDrawing(textBrush, new System.Windows.Media.Pen(), textGeometry));

                    var targetVisual = new DrawingVisual();
                    var targetContext = targetVisual.RenderOpen();
                    targetContext.DrawDrawing(group);
                    var target = new RenderTargetBitmap(thumbnailWidth, thumbnailHeight, 96, 96, PixelFormats.Default);
                    targetContext.Close();

                    target.Render(targetVisual);
                    var targetFrame = BitmapFrame.Create(target);

                    //Convert the bitmap frame to jpg
                    byte[] targetBytes = null;

                    using (var memoryStream = new MemoryStream())
                    {
                        var targetEncoder = new JpegBitmapEncoder()
                        {
                            QualityLevel = 100//quality
                        };

                        targetEncoder.Frames.Add(targetFrame);
                        targetEncoder.Save(memoryStream);
                        targetBytes = memoryStream.ToArray();
                    }

                    return targetBytes;
                }
            }
            else
            {
                return null;
            }
        }

        public static void GetImageSize(byte[] imageData, out int width, out int height)
        {
            if (imageData != null)
            {
                using (var imageStream = new MemoryStream(imageData))
                {
                    //Read the image
                    var bitmapDecoder = BitmapDecoder.Create(imageStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
                    var bitmapFrame = bitmapDecoder.Frames[0];

                    //Init the size
                    width = bitmapFrame.PixelWidth;
                    height = bitmapFrame.PixelHeight;
                }
            }
            else
            {
                width = 0;
                height = 0;
            }
        }

        public static byte[] Crop(byte[] image, int Width, int Height, int X, int Y)
        {
            using (MemoryStream msOriginalImage = new MemoryStream(image))
            {
                using (MemoryStream msCroppedImage = new MemoryStream())
                {

                    Image OriginalImage = Image.FromStream(msOriginalImage);
                    using (SD.Bitmap bmp = new SD.Bitmap(Width, Height, OriginalImage.PixelFormat))
                    {
                        bmp.SetResolution(OriginalImage.HorizontalResolution, OriginalImage.VerticalResolution);
                        using (SD.Graphics Graphic = SD.Graphics.FromImage(bmp))
                        {
                            Graphic.SmoothingMode = SmoothingMode.AntiAlias;
                            Graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
                            Graphic.PixelOffsetMode = PixelOffsetMode.HighQuality;
                            Graphic.DrawImage(OriginalImage, new SD.Rectangle(0, 0, Width, Height), X, Y, Width, Height, SD.GraphicsUnit.Pixel);
                            bmp.Save(msCroppedImage, OriginalImage.RawFormat);
                            byte[] baImage = msCroppedImage.GetBuffer();
                            return baImage;
                        }
                    }
                }
            }
        }

        [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        public static byte[] GetResizedImage(object pictureID, ImageMode imageMode, bool resizeToPortrait)
        {
            return GetResizedImage(pictureID, imageMode, resizeToPortrait, true);
        }

        [EnvironmentPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        public static byte[] GetResizedImage(object pictureID, ImageMode imageMode, bool resizeToPortrait, bool showWatermark)
        {
            Dictionary<string, int> imageSize = Imaging.GetImageSize(imageMode);

            string imageName = pictureID + ".jpg";
            string folderPath = ConfigurationManager.AppSettings["TempPath"];

            string cachePath = folderPath + "\\Cache_" + imageMode.ToString();
            cachePath += (showWatermark) ? "_Watermark" : string.Empty;
            cachePath += (resizeToPortrait) ? "_Portrait" : string.Empty;
            cachePath = HttpContext.Current.Server.MapPath(cachePath);

            string cacheImagePath = cachePath + "\\" + imageName;

            if (!File.Exists(cacheImagePath))
            {
                string imagePath = string.Empty;

                if (pictureID is int)
                {
                    ImagesRepository ir = new ImagesRepository();
                    tSite_Photos_Image img = ir.GetImageByID((int)pictureID);
                    if (img != null)
                    {
                        imagePath = HttpContext.Current.Server.MapPath(ConfigurationManager.AppSettings["GalleryPath"] + img.AID.ToString("D6") + "\\" + img.ImageName + ".jpg");
                    }
                }
                else
                {
                    imagePath = HttpContext.Current.Server.MapPath(ConfigurationManager.AppSettings["StaffPath"] + "\\" + pictureID.ToString() + ".jpg");
                }

                if(imagePath != string.Empty)
                {
                    if (!Directory.Exists(cachePath))
                    {
                        Directory.CreateDirectory(cachePath);
                    }

                    byte[] baCacheImage = Core.Utils.Files.FileToArray(imagePath);
                    baCacheImage = (resizeToPortrait) ? Imaging.ResizeImageToPortrait(baCacheImage, imageSize["W"], imageSize["H"], 100) : Imaging.ResizeImage(baCacheImage, imageSize["W"], imageSize["H"], 100);
                    if (showWatermark)
                    {
                        baCacheImage = Imaging.Watermark(baCacheImage, ConfigurationManager.AppSettings["Watermark"]);
                    }

                    Files.ByteArrayToFile(cacheImagePath, baCacheImage);

                    return baCacheImage;
                }
                else
                {
                    return null;
                }
            }

            return Core.Utils.Files.FileToArray(cacheImagePath);
        }

        public static void DeleteImageCached(object pictureID)
        {
            string folderPath = HttpContext.Current.Server.MapPath(ConfigurationManager.AppSettings["TempPath"]);
            string fileName = pictureID + ".jpg";

            string[] pictures = Directory.GetFiles(folderPath, fileName, SearchOption.AllDirectories);

            foreach (var picture in pictures)
            {
                File.Delete(picture);
            }
        }

        public static Dictionary<string, int> GetImageSize(ImageMode imageMode)
        {
            string h = string.Empty;
            string w = string.Empty;
            Dictionary<string, int> imageSize = new Dictionary<string, int>();

            switch (imageMode)
            {
                case ImageMode.Home:
                    h = ConfigurationManager.AppSettings["ImageResizer.Home.Height"];
                    w = ConfigurationManager.AppSettings["ImageResizer.Home.Width"];
                    break;
                case ImageMode.GalleryThumbnail:
                    h = ConfigurationManager.AppSettings["ImageResizer.GalleryThumbnail.Height"];
                    w = ConfigurationManager.AppSettings["ImageResizer.GalleryThumbnail.Width"];
                    break;
                case ImageMode.GalleryPhoto:
                    h = ConfigurationManager.AppSettings["ImageResizer.GalleryPhoto.Height"];
                    w = ConfigurationManager.AppSettings["ImageResizer.GalleryPhoto.Width"];
                    break;
                case ImageMode.MyAlbumPhoto:
                    h = ConfigurationManager.AppSettings["ImageResizer.MyAlbumPhoto.Height"];
                    w = ConfigurationManager.AppSettings["ImageResizer.MyAlbumPhoto.Width"];
                    break;
                case ImageMode.MyAlbumThumbnail:
                    h = ConfigurationManager.AppSettings["ImageResizer.MyAlbumThumbnail.Height"];
                    w = ConfigurationManager.AppSettings["ImageResizer.MyAlbumThumbnail.Width"];
                    break;
                case ImageMode.Staff:
                    h = ConfigurationManager.AppSettings["ImageResizer.Staff.Height"];
                    w = ConfigurationManager.AppSettings["ImageResizer.Staff.Width"];
                    break;
                case ImageMode.StaffCrop:
                    h = ConfigurationManager.AppSettings["ImageResizer.StaffCrop.Height"];
                    w = ConfigurationManager.AppSettings["ImageResizer.StaffCrop.Width"];
                    break;
                case ImageMode.Avatar:
                    h = ConfigurationManager.AppSettings["ImageResizer.Avatar.Height"];
                    w = ConfigurationManager.AppSettings["ImageResizer.Avatar.Width"];
                    break;
                case ImageMode.Members:
                    h = ConfigurationManager.AppSettings["ImageResizer.Members.Height"];
                    w = ConfigurationManager.AppSettings["ImageResizer.Members.Width"];
                    break;
                case ImageMode.MailThumbnail:
                    h = ConfigurationManager.AppSettings["ImageResizer.MailThumbnail.Height"];
                    w = ConfigurationManager.AppSettings["ImageResizer.MailThumbnail.Width"];
                    break;
                default:
                    throw new Exception("Image mode not defined");
            }

            imageSize.Add("H", Convert.ToInt32(h));
            imageSize.Add("W", Convert.ToInt32(w));

            return imageSize;
        }
        #endregion

        #region Private Methodes
        private static byte[] RotateImage90(byte[] image, EncoderValue ev)
        {
            using (MemoryStream ms = new MemoryStream(image))
            {
                Image img = Image.FromStream(ms);
                SD.Imaging.Encoder enc = SD.Imaging.Encoder.Transformation;
                using (EncoderParameters encParms = new EncoderParameters(2))
                {
                    ImageCodecInfo codecInfo = FindEncoder(ImageFormat.Jpeg);

                    // for lossless rewriting must rotate the image by 90 degrees!
                    encParms.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
                    encParms.Param[0] = new EncoderParameter(enc, (long)ev);
                    using (MemoryStream msImage = new MemoryStream())
                    {
                        // Save to memory using the Jpeg format
                        img.Save(msImage, codecInfo, encParms);

                        img.Dispose();
                        // read to end
                        return msImage.GetBuffer();
                    }
                }
            }
        }

        private static BitmapFrame WpfFastResize(BitmapFrame photo, int width, int height)
        {
            var target = new TransformedBitmap(photo,
                new ScaleTransform(
                    width / photo.Width * 96 / photo.DpiX,
                    height / photo.Height * 96 / photo.DpiY,
                    0, 0));

            return BitmapFrame.Create(target);
        }

        private static BitmapSource CreateImage(byte[] imageData, int decodePixelWidth, int decodePixelHeight)
        {
            if (imageData == null) return null;

            BitmapImage result = new BitmapImage();
            result.BeginInit();
            if (decodePixelWidth > 0)
            {
                result.DecodePixelWidth = decodePixelWidth;
            }
            if (decodePixelHeight > 0)
            {
                result.DecodePixelHeight = decodePixelHeight;
            }
            result.StreamSource = new MemoryStream(imageData);
            result.CreateOptions = BitmapCreateOptions.None;
            result.CacheOption = BitmapCacheOption.Default;
            result.EndInit();
            return result;
        }

        private static ImageCodecInfo FindEncoder(ImageFormat fmt)
        {
            ImageCodecInfo[] infoArray1 = ImageCodecInfo.GetImageEncoders();
            ImageCodecInfo[] infoArray2 = infoArray1;
            for (int num1 = 0; num1 < infoArray2.Length; num1++)
            {
                ImageCodecInfo info1 = infoArray2[num1];
                if (info1.FormatID.Equals(fmt.Guid))
                {
                    return info1;
                }
            }
            return null;
        }
        #endregion
    }
}
